package gates;

import register.Register;
import gates.MatrixGate;
import mathtools.*;
/**
 * Matrix representation of a hadamard gate.
 * This implements Gate interface, so consult this for public methods.
 * The constructor for this gate is protected, so generating such an object is left
 * for the factory class <code>Factory</code>
 * @author Alex Bush, James Hancox
 *
 */
public class HadamardMatrix extends MatrixGate {
 
	/**Matrix representing the classic Hadamard gate:
	 * |1  1|
	 * |1 -1| */
	private Matrix Hadamard;
	/**The matrix representing this gate when applied to the specific target qubit.*/
	private Matrix gate;
	/**int storing which qubit to target*/
	private int targetQ;
	
	/**
    * Protected constructor for a matrix representation of a hadamard gate
    * Requires a target bit for the gate to operate on, however if the target bit is
    * -1 the gate will act on all qubits.
    * @param target - the target bit
    */
	protected HadamardMatrix(int target){
		
		//sets up the basic {{1,1}{1,-1}} Hadamard matrix. 
		Hadamard= new Matrix(2,2,1);
		Hadamard.setElement(1, 1, new Complex(-1,0));
		//stores which is the target bit as an int
		targetQ = target;
		
	}
	
	/**
	 * Applies this gate to the given register. See interface <code>Gate</code> for usage.
	 * TODO info on hadamard gate
	 */
	public void apply(Register r){
		if (targetQ >= r.getNumQbits() || targetQ < -1){
			throw new IllegalArgumentException("Target qubit not in register. Register size (qubits): " + r.getNumQbits() + " Target qubit: " + targetQ);
		}
		/*get number of Qbits from reg*/
		int numQbits = r.getNumQbits();
		//matrix for applying hadamard to whole reg
		if(targetQ==-1){
			gate = Hadamard;
			for(int i=1;i<numQbits;i++){
				gate = gate.tensorProduct(Hadamard);
			}
			
		}
		//matrix for applying Hadamard to the first qubit
		else if (targetQ==0){
			gate = Hadamard;
			if (numQbits>1){
				for(int i=1; i<numQbits;i++){
					gate = gate.tensorProduct(Matrix.IdentityMatrix(2));
				}	
			}
		}
		//matrix for applying hadamard to qubit 2..n
		else{
			gate = Matrix.IdentityMatrix(2);
			for (int i=1; i<targetQ; i++){
				gate = gate.tensorProduct(Matrix.IdentityMatrix(2));
			}
			gate = gate.tensorProduct(Hadamard);
			for (int i=targetQ+1;i<numQbits;i++){
				gate = gate.tensorProduct(Matrix.IdentityMatrix(2));
			}
		}
		//multiply reg by gate to make new reg
		Matrix r_updated = gate.multiplyMatrix(r);
		r.update(r_updated);
		//normalise the vector
		r.normalise();
	}
	
	/**
	 * Returns the name of the gate regardless of representation.
	 */
	public String getName() {
		return "Hadamard";
	}

}
